package com.cloudlead.utils.lang;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.exceptions.JedisConnectionException;

import com.thoughtworks.xstream.XStream;

public class RedisClient {
	private JedisPool jedisPool;

	private XStream xstream;

	public RedisClient(String redisServer) {
		this(redisServer, 3000, 1000, 10000, true, true);
	}

	public RedisClient(String redisServer, int maxActive, int maxIdle,
			int maxWait, boolean testWhileIdle, boolean testOnBorrow) {
		JedisPoolConfig jedisConfig = new JedisPoolConfig();
		jedisConfig.setTestWhileIdle(testWhileIdle);
		jedisConfig.setMaxTotal(maxActive);
		jedisConfig.setMaxIdle(maxIdle);
		jedisConfig.setMaxWaitMillis(maxWait);
		jedisConfig.setTestOnBorrow(testOnBorrow);
		jedisPool = new JedisPool(jedisConfig, redisServer);

		xstream = new XStream();
	}

	/**
	 * 设置值
	 *
	 * @param key
	 *            键
	 * @param value
	 *            值
	 * @param seconds
	 *            到期时间秒数
	 */
	public void set(final String key, final Object value, final int seconds) {

		if (null == key) {
			throw new IllegalArgumentException("Cached key cannot be null.");
		}
		if (null == value) {
			throw new IllegalArgumentException("Cached value cannot be null.");
		}

		execute(new Execution() {

			@Override
			public Object process(Jedis jedis) {
				byte[] byteArray = serialize(value);
				jedis.set(key.getBytes(), byteArray);
				jedis.expire(key.getBytes(), seconds);
				return null;
			}

		});
	}

	/**
	 * 设置值
	 *
	 * @param key
	 *            键
	 * @param value
	 *            值
	 */
	public void set(final String key, final Object value) {
		if (null == key) {
			throw new IllegalArgumentException("Cached key cannot be null.");
		}
		if (null == value) {
			throw new IllegalArgumentException("Cached value cannot be null.");
		}

		execute(new Execution() {

			@Override
			public Object process(Jedis jedis) {
				byte[] byteArray = serialize(value);
				jedis.set(key.getBytes(), byteArray);
				return null;
			}

		});

	}

	/**
	 * 设置到期值
	 *
	 * @param key
	 *            键
	 * @param seconds
	 *            到期时间秒数
	 */
	public void expire(final String key, final int seconds) {
		if (null == key) {
			throw new IllegalArgumentException("Cached key cannot be null.");
		}

		execute(new Execution() {

			@Override
			public Object process(Jedis jedis) {
				jedis.expire(key.getBytes(), seconds);
				return null;
			}

		});
	}

	/**
	 * 获取值
	 *
	 * @param key
	 *            键
	 * @return
	 */
	public Object get(final String key) {

		if (null == key) {
			throw new IllegalArgumentException("Cached key cannot be null.");
		}

		return execute(new Execution() {

			@Override
			public Object process(Jedis jedis) {
				byte[] bs = jedis.get(key.getBytes());
				if (null == bs) {
					return null;
				} else {
					return deserialize(bs);
				}
			}

		});

	}

	/**
	 * 检测值是否存在
	 *
	 * @param key
	 *            键
	 * @return
	 */
	public boolean exists(final String key) {
		if (null == key) {
			throw new IllegalArgumentException("Cached key cannot be null.");
		}

		return (Boolean) execute(new Execution() {

			@Override
			public Object process(Jedis jedis) {
				Boolean exists = jedis.exists(key.getBytes());
				return exists;
			}

		});

	}

	/**
	 * 删除值
	 *
	 * @param key
	 *            键
	 */
	public void del(final String key) {
		if (null == key) {
			throw new IllegalArgumentException("Cached key cannot be null.");
		}

		execute(new Execution() {

			@Override
			public Object process(Jedis jedis) {
				jedis.del(key.getBytes());
				return null;
			}

		});
	}

	/**
	 * 在key对应list的头部添加元素
	 *
	 * @param key
	 *            键
	 * @param value
	 *            值
	 * @return
	 */
	public Long lpush(final String key, final Object value) {
		if (null == key) {
			throw new IllegalArgumentException("Cached key cannot be null.");
		}
		if (null == value) {
			throw new IllegalArgumentException("Cached value cannot be null.");
		}

		return (Long) execute(new Execution() {

			@Override
			public Object process(Jedis jedis) {
				byte[] byteArray = serialize(value);
				Long index = jedis.lpush(key.getBytes(), byteArray);
				return index;
			}

		});
	}

	/**
	 * 在key对应list的尾部添加元素
	 *
	 * @param key
	 *            键
	 * @param value
	 *            值
	 * @return
	 */
	public Long rpush(final String key, final Object value) {
		if (null == key) {
			throw new IllegalArgumentException("Cached key cannot be null.");
		}
		if (null == value) {
			throw new IllegalArgumentException("Cached value cannot be null.");
		}

		return (Long) execute(new Execution() {

			@Override
			public Object process(Jedis jedis) {
				byte[] byteArray = serialize(value);
				Long index = jedis.rpush(key.getBytes(), byteArray);
				return index;
			}

		});
	}

	/**
	 * 从list的头部删除元素，并返回删除元素
	 *
	 * @param key
	 *            键
	 * @return
	 */
	public Object lpop(final String key) {

		if (null == key) {
			throw new IllegalArgumentException("Cached key cannot be null.");
		}

		return execute(new Execution() {

			@Override
			public Object process(Jedis jedis) {
				byte[] bs = jedis.lpop(key.getBytes());
				if (null == bs) {
					return null;
				} else {
					return deserialize(bs);
				}
			}

		});

	}

	/**
	 * 从list的尾部删除元素，并返回删除元素
	 *
	 * @param key
	 *            键
	 * @return
	 */
	public Object rpop(final String key) {

		if (null == key) {
			throw new IllegalArgumentException("Cached key cannot be null.");
		}

		return execute(new Execution() {

			@Override
			public Object process(Jedis jedis) {
				byte[] bs = jedis.rpop(key.getBytes());
				if (null == bs) {
					return null;
				} else {
					return deserialize(bs);
				}
			}

		});

	}

	/**
	 * 从list的头部删除元素，并返回删除元素。如果没有元素，则一直等待
	 *
	 * @param key
	 *            键
	 * @return
	 */
	public Object blpop(final String key) {

		if (null == key) {
			throw new IllegalArgumentException("Cached key cannot be null.");
		}

		return execute(new Execution() {

			@Override
			public Object process(Jedis jedis) {
				List<byte[]> bss = jedis.blpop(0, key.getBytes());
				if (null == bss || bss.size() < 2) {
					return null;
				} else {
					return deserialize(bss.get(1));
				}
			}

		});

	}

	/**
	 * 从list的尾部删除元素，并返回删除元素。如果没有元素，则一直等待
	 *
	 * @param key
	 *            键
	 * @return
	 */
	public Object brpop(final String key) {

		if (null == key) {
			throw new IllegalArgumentException("Cached key cannot be null.");
		}

		return execute(new Execution() {

			@Override
			public Object process(Jedis jedis) {
				List<byte[]> bss = jedis.brpop(0, key.getBytes());
				if (null == bss || bss.size() < 2) {
					return null;
				} else {
					return deserialize(bss.get(1));
				}
			}

		});

	}

	/**
	 * 疏通堵塞
	 *
	 * @param key
	 */
	public void unblock(final String key) {
		lpush(key, new NullValue());
	}

	/**
	 * 返回key对应list的长度
	 *
	 * @param key
	 *            键
	 * @return
	 */
	public Long llen(final String key) {

		if (null == key) {
			throw new IllegalArgumentException("Cached key cannot be null.");
		}

		return (Long) execute(new Execution() {

			@Override
			public Object process(Jedis jedis) {
				Long len = jedis.llen(key.getBytes());
				return len;
			}

		});

	}

	/**
	 * 根据键值模糊查询
	 *
	 * @param key
	 *            键值
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public Map<String, Object> findByKeyLike(final String key) {
		if (null == key) {
			throw new IllegalArgumentException("Cached key cannot be null.");
		}

		return (Map<String, Object>) execute(new Execution() {

			@Override
			public Object process(Jedis jedis) {
				Set<byte[]> keys = jedis.keys(key.getBytes());
				Map<String, Object> answer = new LinkedHashMap<>();

				for (byte[] bs : keys) {
					byte[] vbs = jedis.get(bs);
					if (null == vbs) {
						answer.put(new String(bs), null);
					} else {
						answer.put(new String(bs), deserialize(vbs));
					}
				}
				return answer;
			}

		});
	}

	private static interface Execution {
		public Object process(Jedis jedis);
	}

	private static class NullValue {

	}

	/**
	 * 执行请求，开始前获取资源，结束后返回资源
	 *
	 * @param execution
	 * @return
	 */
	public Object execute(Execution execution) {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			Object result = execution.process(jedis);
			return result;
		} catch (JedisConnectionException e) {
			if (null != jedis) {
				jedisPool.returnBrokenResource(jedis);
				jedis = null;
			}
			throw e;
		} finally {
			if (null != jedis) {
				jedisPool.returnResource(jedis);
				jedis = null;
			}
		}
	}

	private Object deserialize(byte[] data) {
		Object fromXML = xstream.fromXML(new String(data));
		if (fromXML instanceof NullValue) {
			return null;
		}
		return fromXML;
	}

	private byte[] serialize(final Object value) {
		return xstream.toXML(value).getBytes();
	}

	public static void main(String[] args) {
		RedisClient redisExecution = new RedisClient("172.16.130.250");
		redisExecution.rpush("test123456", "123");
		redisExecution.rpush("test123456", new NullValue());

		Object blpop = redisExecution.blpop("test123456");
		System.out.println(blpop);

		Object blpop1 = redisExecution.blpop("test123456");
		System.out.println(blpop1);
	}
}
